#include <buola/scene/cscene.h>
#include <buola/scene/cobject.h>
#include <buola/scene/transform/crigidbody.h>
#include <buola/scene/transform/csoftbody.h>
#include <btBulletDynamicsCommon.h>
#include <BulletSoftBody/btSoftRigidDynamicsWorld.h>
#include <BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>

namespace buola { namespace scene {

struct CScene::SPhysics
{
    SPhysics(bool pSoftBodies,bool pGravity)
    {
        mBroadphase=new btDbvtBroadphase;
        if(pSoftBodies)
            mCollisionConfiguration=new btSoftBodyRigidBodyCollisionConfiguration;
        else
            mCollisionConfiguration=new btDefaultCollisionConfiguration;
        mCollisionDispatcher=new btCollisionDispatcher(mCollisionConfiguration);
        mSolver=new btSequentialImpulseConstraintSolver;

        if(pSoftBodies)
        {
            mSoftWorld=new btSoftRigidDynamicsWorld(mCollisionDispatcher,mBroadphase,mSolver,mCollisionConfiguration);
            mWorld=mSoftWorld;
        }
        else
        {
            mWorld=new btDiscreteDynamicsWorld(mCollisionDispatcher,mBroadphase,mSolver,mCollisionConfiguration);
        }

        if(pGravity)
            mWorld->setGravity(btVector3(0,0,-9.818)); //gravity in Stockholm :)
        else
            mWorld->setGravity(btVector3(0,0,0));
        
        if(pSoftBodies)
        {
            mSoftWorld->getWorldInfo().m_gravity=mWorld->getGravity();
        }
    }
    
    ~SPhysics()
    {
        delete mWorld;
        delete mSolver;
        delete mCollisionDispatcher;
        delete mCollisionConfiguration;
        delete mBroadphase;
    }
    
    btBroadphaseInterface *mBroadphase;
    btDefaultCollisionConfiguration *mCollisionConfiguration;
    btCollisionDispatcher *mCollisionDispatcher;
    btSequentialImpulseConstraintSolver *mSolver;
    btDiscreteDynamicsWorld *mWorld;
    btSoftRigidDynamicsWorld *mSoftWorld;
};
    
CScene::CScene(bool pSoftBodies,bool pGravity)
    :   mSoftBodies(pSoftBodies)
    ,   mGravity(pGravity)
{
    mWorld=new CWorld;
}

CScene::~CScene()
{

}

void CScene::Render(CRenderContext &pContext)
{
    mWorld->Render(pContext);
}

void CScene::CollectPolygons(std::vector<mat::CVec3d> &pPoints,std::vector<mat::CVec3d> &pNormals,std::vector<int> &pFaces)
{
    mWorld->CollectPolygons(pPoints,pNormals,pFaces);
}

void CScene::Update()
{
    mWorld->Update();
}

void CScene::AddBody(PRigidBody pBody)
{
    Physics().mWorld->addRigidBody(pBody->GetBulletBody().get());
}

void CScene::AddBody(PSoftBody pBody)
{
    pBody->OnScene(this);
    Physics().mSoftWorld->addSoftBody(pBody->GetBulletBody().get());
    mSoftBodyList.push_back(pBody);
}

void CScene::AddConstraint(btTypedConstraint* pConstraint)
{
    Physics().mWorld->addConstraint(pConstraint,true);
}

void CScene::StepPhysics(double pTime,int pMaxSteps,double pStepTime)
{
    start_timer();
    Physics().mWorld->stepSimulation(pTime,pMaxSteps,pStepTime);
    end_timer("physics simulation");
}

btSoftBodyWorldInfo &CScene::SoftBodyWorldInfo()
{
    return Physics().mSoftWorld->getWorldInfo();
}

CScene::SPhysics &CScene::Physics()
{
    if(!mPhysics)
        mPhysics.reset(new SPhysics(mSoftBodies,mGravity));
    
    return *mPhysics;
}

/*namespace scene*/ } /*namespace buola*/ }
